package com.pfsoft.security.self;


import org.apache.commons.codec.binary.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;

/**
 * @author pengfei
 * @date 2022年07月27日 9:48
 */

public class RsaUtil {
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    private static final String ALGORITHM_NAME = "RSA";
    private static final String MD5_RSA = "MD5withRSA";
    private static KeyPair keyPair;

    public RsaUtil() {
        new RsaUtil(ALGORITHM_NAME, 1024);
    }

    public RsaUtil(String algorithmName, int size) {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithmName);
            keyPairGenerator.initialize(size);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加密操作
     *
     * @param originalMessage 明文信息
     * @return 密文
     */
    public String encryption(String originalMessage) {
        String encryptMessage = "";
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(ALGORITHM_NAME);
            //选择公钥进行加密
            cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
            int inputLen = originalMessage.getBytes().length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offset = 0;
            byte[] cache;
            int segment = 0;
            // 对数据分段加密
            while (inputLen - offset > 0) {
                if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(originalMessage.getBytes(), offset, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(originalMessage.getBytes(), offset, inputLen - offset);
                }
                out.write(cache, 0, cache.length);
                segment++;
                offset = segment * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            encryptMessage = new String(Base64.encodeBase64(encryptedData));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return encryptMessage;
    }


    /**
     * 解密操作
     *
     * @param data base64编码后的密文
     * @return 明文
     */
    public String decryption(String data) {
        String descryptMessage = "";
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
            cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
            //因为加密后的byte[]是通过base64编码后创建的字符串，因此解密时也需要先进行base64逆向操作decode
            byte[] dataBytes = Base64.decodeBase64(data);
            int length = dataBytes.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offset = 0;
            byte[] cache;
            int segment = 0;
            while (length - offset > 0) {    //表示还有没读完的，需要继续读
                if (length - offset > MAX_DECRYPT_BLOCK) {    //看本次读取是否有必要分段, 要读的长度>单次读取缓存长度则读一个缓存长度的内容
                    cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
                } else { //否则，本次全部读完
                    cache = cipher.doFinal(dataBytes, offset, length - offset);
                }
                // 将指定的字节写入此字节数组输出流，注意是把cache缓存的内容输出到outputStream中
                out.write(cache, 0, cache.length);
                segment++;
                offset = segment * MAX_DECRYPT_BLOCK;
            }
            byte[] bytes = out.toByteArray();
            out.close();
            descryptMessage = new String(bytes,StandardCharsets.UTF_8);

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return descryptMessage;
    }

}
